package com.rqb.redisson.controller;

import org.apache.log4j.Logger;
import org.redisson.Redisson;
import org.redisson.RedissonMultiLock;
import org.redisson.api.RLock;
import org.redisson.api.RReadWriteLock;
import org.redisson.config.Config;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PropertiesLoaderUtils;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.net.URL;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;

/**
 * Created by baiguantao on 2017/4/14.
 * 提供默认可重入锁、读写锁、公平锁、联锁（分布式）
 *
 * 利用springmvc单例的方式对redisson进行初始化
 * 可以开放Component获取单例方式
 * 也对定时任务进行开启（定时清除重复队列信息）
 *
 */
//@Component
public class DistributedRedisLock {
    private static final Logger logger = Logger.getLogger(DistributedRedisLock.class);
    private static ReentrantLock lock=new ReentrantLock();
    private static final String LOCK_TITLE = "redisLock_";
    private static volatile boolean RLOCK_FLAG = false;
    //基础redis信息
    private static Config config = new Config();
    private static Redisson redisson = null;

    /**
     * 初始化redis和Redisson信息
     */
    {
        try {
            System.out.println("初始化");
            Resource resource = null;
            Properties props = null;
            String address = null;
            resource = new ClassPathResource("/redis.properties");
            props = PropertiesLoaderUtils.loadProperties(resource);
            address= (String) props.get("address");

                  /*
                  Set<URL> set=new HashSet<URL>();
            set.add(new URL("127.0.0.1:6380"));
            config.useMasterSlaveServers().setMasterAddress("127.0.0.1:6379").setSlaveAddresses(set);


                  config.useClusterServers() //这是用的集群server
                        .setScanInterval(2000) //设置集群状态扫描时间
                        .setMasterConnectionPoolSize(10000) //设置连接数
                        .setSlaveConnectionPoolSize(10000)
                        .addNodeAddress("127.0.0.1:6379","127.0.0.1:6380");*/
            logger.info("address set begin");
            logger.info(address);
            config.useSingleServer().setAddress(address);
            logger.info("2");
            redisson = (Redisson) Redisson.create(config);
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
    /**
     * 获取不同实例的分布式锁
     *  暂时只支持3个
     */
    public static void acquireMultiLock(Redisson redisson1, String lockName1,
                                        Redisson redisson2, String lockName2,
                                        Redisson redisson3, String lockName3){
        RLock lock1 = redisson1.getLock(lockName1);
        RLock lock2 = redisson2.getLock(lockName2);
        RLock lock3 = redisson3.getLock(lockName3);
        // 同时加锁：lock1 lock2 lock3
        // 所有的锁都上锁成功才算成功。
        RedissonMultiLock lock = new RedissonMultiLock(lock1, lock2, lock3);
        lock.lock();
    }

   /* *//**
     * 获取不同实例的分布式锁
     * String 存lockName，Object存实例对象
     * redissons lockNames长度一直  顺序一致
     *//*
    public static void acquireMultiLock(Map<String,Object> redissons){
        Set<String> sets=redissons.keySet();
        List<RLock> rLocks=new ArrayList<RLock>();
        for (String lockName:sets) {
            Redisson redisson =(Redisson)redissons.get(lockName);
            RLock lock= redisson.getLock(lockName);
            rLocks.add(lock);
        }
       *//* RLock lock1 = redisson1.getLock(lockName1);
        RLock lock2 = redisson2.getLock(lockName2);
        RLock lock3 = redisson3.getLock(lockName3);*//*
        // 同时加锁：lock1 lock2 lock3
        // 所有的锁都上锁成功才算成功。
        RedissonMultiLock lock = new RedissonMultiLock(lock1, lock2, lock3);
        lock.lock();
    }*/
    /**
     * 释放不同实例的分布式锁
     */
    public static void realeaseMultiLock(Redisson redisson1,Redisson redisson2,Redisson redisson3,
                                        String lockName1,String lockName2,String lockName3){
        RLock lock1 = redisson1.getLock(lockName1);
        RLock lock2 = redisson2.getLock(lockName2);
        RLock lock3 = redisson3.getLock(lockName3);
        RedissonMultiLock lock = new RedissonMultiLock(lock1, lock2, lock3);
        lock.unlock();
    }
    /**
     * 获取公平锁
     */
    public static void acquireFairLock(String lockName){
        String key = LOCK_TITLE + lockName;
            RLock fairLock = redisson.getFairLock(key);
            fairLock.lock();
            fairLock.lock(10, TimeUnit.SECONDS);
    }
    /**
     * 释放公平锁
     */
    public static void realeaseFairLock(String lockName){
        String key = LOCK_TITLE + lockName;
        RLock fairLock = redisson.getFairLock(key);
        fairLock.unlock();
    }
    /**
     * 获取读写锁
     * true 是读锁
     */
    public static void acquireReadWrite(String lockName,boolean isRead){
            String key = LOCK_TITLE + lockName;
            RReadWriteLock rwlock = redisson.getReadWriteLock(key);
            if (isRead) {
                rwlock.readLock().lock();
                rwlock.readLock().lock(10, TimeUnit.SECONDS);
            } else {
                rwlock.writeLock().lock();
                rwlock.writeLock().lock(10, TimeUnit.SECONDS);
            }
        System.err.println("======lock======"+Thread.currentThread().getName());
    }
    /**
     * 释放读写锁
     * true 是读锁
     */
    public static void releaseReadWrite(String lockName,boolean isRead){
        String key = LOCK_TITLE + lockName;
        RReadWriteLock rwlock = redisson.getReadWriteLock(key);
        if (isRead) {
            rwlock.readLock().unlock();

        } else {
            rwlock.writeLock().unlock();
        }
        System.err.println("======lock======"+Thread.currentThread().getName());
    }
    /**
     * 锁名称用完就释放的方式这里是并发争抢的情况  并发执行保持一致性无序的执行
     * ???如何防止重复进入呢？
     *
     *
     String key = "test123";
     DistributedRedisLock.acquire(key);
     //Thread.sleep(10); //获得锁之后可以进行相应的处理
     System.err.println("======获得锁后进行相应的操作======");
     DistributedRedisLock.release(key);
     System.err.println("=============================");
     * @param lockName
     */
    public static void acquire(String lockName){
        lock.lock();
        String key = LOCK_TITLE + lockName;
        RLock mylock = redisson.getLock(key);
        mylock.lock(2, TimeUnit.MINUTES); //lock提供带timeout参数，timeout结束强制解锁，防止死锁
        System.err.println("======lock======"+Thread.currentThread().getName());
            lock.unlock();
    }

    private static ConcurrentHashMap<String,Object> unMutil=new ConcurrentHashMap<String, Object>();
    static {
        //定时清除上述锁
        new TimerManager();
    }
    /**
     * if(DistributedRedisLock.acquireUnMutil(key)) {
     //Thread.sleep(10); //获得锁之后可以进行相应的处理
     System.err.println("======获得锁后进行相应的操作======");
     //DistributedRedisLock.release(key);
     //System.err.println("=============================");
     }
     * 不重复的执行
     * @param lockName
     */
    public static boolean acquireUnMutil(String lockName){
        lock.lock();
        if (unMutil.containsKey(lockName)) {
            logger.info("已经有了  不再执行这个 防止重复");
            lock.unlock();
            return false;
        } else {
            unMutil.putIfAbsent(lockName,lockName);
            String key = LOCK_TITLE + lockName;
            RLock mylock = redisson.getLock(key);
            mylock.lock(2, TimeUnit.MINUTES); //lock提供带timeout参数，timeout结束强制解锁，防止死锁
            System.err.println("======lock======"+Thread.currentThread().getName());
            lock.unlock();
            return true;
        }

    } /**
     * 不重复的释放
     * @param lockName
     */
    public static void releaseUnMutil(String lockName){
        lock.lock();
        unMutil.remove(lockName,lockName);
        String key = LOCK_TITLE + lockName;
        logger.info("锁是什么："+key);
        RLock mylock = redisson.getLock(key);
        mylock.unlock();
        unMutil.remove(lockName);
        lock.unlock();
    }

    /**
     * 锁名称用完就释放但是要比对的
     * type  1 则放入队列中 表示已经成功,
     *
     * @param lockName
     */
    public static void acquire(String lockName,String type){
        if (type==null)type="1";
        if (type.equals("1")) {
        }
        String key = LOCK_TITLE + lockName;
        RLock mylock = redisson.getLock(key);
        mylock.lock(2, TimeUnit.MINUTES); //lock提供带timeout参数，timeout结束强制解锁，防止死锁
        System.err.println("======lock======"+Thread.currentThread().getName());
    }

    public static void release(String lockName){
        String key = LOCK_TITLE + lockName;
        logger.info("锁是什么："+key);
        RLock mylock = redisson.getLock(key);
        mylock.unlock();
        RLOCK_FLAG=mylock.isLocked();
        System.err.println("======unlock======"+Thread.currentThread().getName());
    }

    /**
     * 充入锁状态
     * @return
     */
    public static synchronized  boolean isRlockFlag() {
        return RLOCK_FLAG;
    }

    public static void setRlockFlag(boolean rlockFlag) {
        RLOCK_FLAG = rlockFlag;
    }
    public static synchronized  ConcurrentHashMap getUnMutil() {
        return unMutil;
    }
}

class Task extends TimerTask {
    private static final Logger logger = Logger.getLogger(Task.class);
    public void run() {
        ConcurrentHashMap<String,Object> unmutil=DistributedRedisLock.getUnMutil();
        Iterator<Map.Entry<String, Object>> iterator = unmutil.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry<String, Object> entry = iterator.next();
            unmutil.remove(entry.getKey());
        }
        logger.info("清除缓存成功");
    }
}

class TimerManager {
    private static final Logger logger = Logger.getLogger(DistributedRedisLock.class);

    //时间间隔(一天)
    private static final long PERIOD_DAY = 24 * 60 * 60 * 1000;
    public TimerManager() {
        logger.info("定时任务开启");
        Calendar calendar = Calendar.getInstance();
        calendar.set(Calendar.HOUR_OF_DAY, 1); //凌晨1点
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        Date date=calendar.getTime(); //第一次执行定时任务的时间
        //如果第一次执行定时任务的时间 小于当前的时间
        //此时要在 第一次执行定时任务的时间加一天，以便此任务在下个时间点执行。如果不加一天，任务会立即执行。
        if (date.before(new Date())) {
            date = this.addDay(date, 1);
        }
        Timer timer = new Timer();
        Task task = new Task();
        //安排指定的任务在指定的时间开始进行重复的固定延迟执行。
        timer.schedule(task,date,PERIOD_DAY);
    }
    // 增加或减少天数
    public Date addDay(Date date, int num) {
        Calendar startDT = Calendar.getInstance();
        startDT.setTime(date);
        startDT.add(Calendar.DAY_OF_MONTH, num);
        return startDT.getTime();
    }

}
